package org.yeasy.cud.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

/**
 * The type Generic util.
 *
 * @author yangyishe
 * @date 2023年02月03日 16:39
 */
public class GenericUtil {
    /**
     * 获取类中成员变量的泛型类型信息
     *
     * @param clazz     the clazz
     * @param fieldName the field name
     * @return the list
     * @throws NoSuchFieldException the no such field exception
     * @author yangyishe
     * @date 2023年02月03日 16:39
     */
    public static List<Class<?>> calcMemberVariables2Generic(Class<?> clazz, String fieldName) throws NoSuchFieldException {
        // 获取MyTestClass类中名为"list"的字段
        Field listField = clazz.getDeclaredField(fieldName);
        // 获取该字段的类型信息，getGenericType()方法能够获取带有泛型的类型信息
        Type genericType = listField.getGenericType();
        // 但我们实际上需要获取返回值类型中的泛型信息，所以要进一步判断，即判断获取的返回值类型是否是参数化类型ParameterizedType
        List<Class<?>> lstGenericClass = new ArrayList<>();
        if (genericType instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            // 获取成员变量的泛型类型信息
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                Class<?> fieldArgClass = (Class<?>) actualTypeArgument;
//                System.out.println("成员变量的泛型信息：" + fieldArgClass);
                lstGenericClass.add(fieldArgClass);
            }
        }
        return lstGenericClass;
    }

    /**
     * 获取方法参数的泛型类型信息
     *
     * @param clazz      the clazz
     * @param methodName the method name
     * @param paramClass the param class
     * @return the list
     * @throws NoSuchMethodException the no such method exception
     * @author yangyishe
     * @date 2023年02月03日 16:39
     */
    public static List<Class<?>> calcMethodParametric2Generic(Class<?> clazz, String methodName, Class<?> paramClass) throws NoSuchMethodException {
        // 获取MyTestClass类中名为"setList"的方法
        Method setListMethod = clazz.getDeclaredMethod(methodName, paramClass);
        // 获取该方法的参数类型信息（带有泛型）
        Type[] genericParameterTypes = setListMethod.getGenericParameterTypes();
        // 但我们实际上需要获取返回值类型中的泛型信息，所以要进一步判断，即判断获取的返回值类型是否是参数化类型ParameterizedType
        List<Class<?>> lstGenericClass = new ArrayList<>();
        for (Type genericParameterType : genericParameterTypes) {
            ParameterizedType parameterizedType = (ParameterizedType) genericParameterType;
            // 获取成员方法参数的泛型类型信息
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                Class<?> realType = (Class<?>) actualTypeArgument;
//                System.out.println("成员方法参数的泛型信息：" + realType);
                lstGenericClass.add(realType);
            }
        }
        return lstGenericClass;
    }

    /**
     * 获取方法返回值的泛型类型信息
     *
     * @param clazz      the clazz
     * @param methodName the method name
     * @return the list
     * @throws NoSuchMethodException the no such method exception
     * @author yangyishe
     * @date 2023年02月03日 16:39
     */
    public static List<Class<?>> calcMethodReturnValue2Generic(Class<?> clazz, String methodName) throws NoSuchMethodException {
        // 获取名为"getList"的方法，在MyClass类中
        Method getListMethod = clazz.getDeclaredMethod(methodName);
        // 获取返回值类型，getGenericReturnType()会返回值带有泛型的返回值类型
        Type genericReturnType = getListMethod.getGenericReturnType();
        // 但我们实际上需要获取返回值类型中的泛型信息，所以要进一步判断，即判断获取的返回值类型是否是参数化类型ParameterizedType
        List<Class<?>> lstGenericClass = new ArrayList<>();
        if (genericReturnType instanceof ParameterizedType) {
            // 如果要使用ParameterizedType中的方法，必须先强制向下转型
            ParameterizedType type = (ParameterizedType) genericReturnType;
            // 获取返回值类型中的泛型类型，因为可能有多个泛型类型，所以返回一个数组
            Type[] actualTypeArguments = type.getActualTypeArguments();
            // 循环数组，遍历每一个泛型类型
            for (Type actualTypeArgument : actualTypeArguments) {
                Class<?> typeArgClass = (Class<?>) actualTypeArgument;
//                System.out.println("成员方法返回值的泛型信息：" + typeArgClass);
                lstGenericClass.add(typeArgClass);
            }
        }
        return lstGenericClass;
    }
}
